package com.sword.demo.concurrent.interfaces.api;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;

/**
 * 多线程Api
 * @author sword
 * @date 2022/3/1 20:08
 */
@RestController
@RequestMapping("/concurrent")
@Slf4j
@Api(tags = {"多线程Api"})
public class ConcurrentApi {

    /**
     * 简单线程池
     */
    private final Executor simpleExecutor;

    /**
     * 构造函数
     * @param simpleExecutor 简单线程池
     * @author sword
     * @date 2022/3/1 20:23
     */
    public ConcurrentApi(@Qualifier("simpleExecutor") Executor simpleExecutor) {
        this.simpleExecutor = simpleExecutor;
    }

    /**
     * 运行线程
     * @author sword
     * @date 2022/3/1 20:23
     */
    @GetMapping("/runThread")
    @ResponseStatus(HttpStatus.OK)
    @ApiOperation("运行线程")
    public void runThread() throws ExecutionException, InterruptedException {
        log.info("主线程开始");
        log.info("主线程的header：{}", getUserCode());

        // 开两个任务线程，均取不到request的header中的工号
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                            log.info("任务线程1开始");
                            log.info("任务线程1的header：{}", getUserCode());
                        })
                        .thenRun(() -> log.info("任务线程1结束")),
                CompletableFuture.runAsync(() -> {
                            log.info("任务线程2开始");
                            log.info("任务线程2的header：{}", getUserCode());
                        })
                        .thenRun(() -> log.info("任务线程2结束"))
        )
        .get();

        log.info("主线程结束");
    }

    /**
     * 使用InheritableThreadLocal共享Request
     * 这种方式有问题，高并发的情况下会出现共享的RequestAttributes中的参数的值都为null的情况
     * @author sword
     * @date 2022/3/1 20:23
     */
    @GetMapping("/shareRequestByInheritableThreadLocal")
    @ResponseStatus(HttpStatus.OK)
    @ApiOperation("使用InheritableThreadLocal共享Request")
    public void shareRequestByInheritableThreadLocal() throws ExecutionException, InterruptedException {
        log.info("主线程开始");
        final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        log.info("主线程的header：{}", getUserCode());

        // 新开一个线程来共享Request，防止操作到主线程的RequestAttributes
        CompletableFuture.runAsync(() -> {
            log.info("共享线程开始");
            // 将主线程的RequestAttributes设置到共享线程中，并且是可继承，也就是说在共享线程中创建的所有子线程都能共享RequestAttributes
            RequestContextHolder.setRequestAttributes(requestAttributes, true);
            log.info("共享线程的header：{}", getUserCode());

            // 再开两个任务线程，并等它们结束
            CompletableFuture.allOf(
                    CompletableFuture.runAsync(() -> {
                        log.info("任务线程1开始");
                        log.info("任务线程1的header：{}", getUserCode());
                    }, simpleExecutor).thenRun(() -> log.info("任务线程1结束")),
                    CompletableFuture.runAsync(() -> {
                        log.info("任务线程2开始");
                        log.info("任务线程2的header：{}", getUserCode());

                    }, simpleExecutor).thenRun(() -> log.info("批量线程2结束")))
            .join();
        })
        // 清空共享线程中的RequestAttributes
        .thenRun(RequestContextHolder::resetRequestAttributes)
        .thenRun(() -> log.info("共享线程结束"))
        .get();

        log.info("主线程结束");
    }

    /**
     * 使用ThreadLocal共享Request
     * @author sword
     * @date 2022/3/1 20:23
     */
    @GetMapping("/shareRequestByThreadLocal")
    @ResponseStatus(HttpStatus.OK)
    @ApiOperation("使用ThreadLocal共享Request")
    public void shareRequestByThreadLocal() throws ExecutionException, InterruptedException {
        log.info("主线程开始");
        final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        log.info("主线程的header：{}", getUserCode());

        // 开两个任务线程，先分别为两个线程设置主线程的RequestAttributes，然后再执行对应的任务，并等它们结束
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> RequestContextHolder.setRequestAttributes(requestAttributes),
                        simpleExecutor)
                    .thenRun(() -> {
                        log.info("任务线程1开始");
                        log.info("任务线程1的header：{}", getUserCode());
                    })
                    .thenRun(() -> log.info("任务线程1结束")),
                CompletableFuture.runAsync(() -> RequestContextHolder.setRequestAttributes(requestAttributes),
                        simpleExecutor)
                    .thenRun(() -> {
                        log.info("任务线程2开始");
                        log.info("任务线程2的header：{}", getUserCode());
                    })
                    .thenRun(() -> log.info("任务线程2结束"))
        )
        .get();

        log.info("主线程结束");
    }

    /**
     * 获取Request的Header中的userCode
     * @return java.lang.String
     * @author sword
     * @date 2022/3/1 20:23
     */
    private String getUserCode() {
        return Optional.ofNullable((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .map(ServletRequestAttributes::getRequest)
                .map(request -> request.getHeader("userCode"))
                .orElse(null);
    }

}
